﻿/****************************************************
 Copyright (C), 2005-2008,eFish Studio Co.,Ltd.
 Website: http://www.eFishStudio.com
 
 File: SQLHibernateDaoSupport.cs
 Author: Abel Yu   Version: 1.0 
 Date: 2006_12_06
 Description:
	
 ****************************************************/
using System;
using System.Collections.Generic;
using System.Data;

using efish.Framework.Common;

using NHibernate.Engine;
using Spring.Data.NHibernate.Generic;
using NHibernate;

namespace efish.Framework.Dao
{
    public class HibernateTemplateEx : HibernateTemplate
    {
        public HibernateTemplateEx()
        {
            //
            // TODO: 在此处添加构造函数逻辑
            //
        }

        public IPageList<T> LoadPageByType<T>( int pages,
                                               int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(
                new LoadLimitAllByTypeHibernateCallback<T>(this, pages, linePerPage));
        }

        #region Find entitys of pagers

        public IPageList<T> FindPageByNamedQuery<T>(string query,
                                               int pages,
                                               int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(new GetLimitHibernateCallback<T>(query, pages, linePerPage, false));
        }

        public IPageList<T> FindPageByNamedQuery<T>(string query,
                                               string parameter,
                                               Object value,
                                               int pages,
                                               int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(new GetLimitHibernateCallback<T>(query, parameter, value, pages, linePerPage, false));
        }

        public IPageList<T> FindPageByNamedQuery<T>(string query,
                                                   string[] parameters,
                                                   Object[] values,
                                                   int pages,
                                                   int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(new GetLimitHibernateCallback<T>(query, parameters, values, pages, linePerPage, false));
        }
        #endregion

        #region Find entitys of pagers by HQL

        public IPageList<T> FindPageByHQL<T>(string queryStr,
                                               int pages,
                                               int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(new GetLimitHibernateCallback<T>(queryStr, pages, linePerPage, true));
        }

        public IPageList<T> FindPageByHQL<T>(string queryStr,
                                               string parameter,
                                               Object value,
                                               int pages,
                                               int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(new GetLimitHibernateCallback<T>(queryStr, parameter, value, pages, linePerPage, true));
        }

        public IPageList<T> FindPageByHQL<T>(string queryStr,
                                                   string[] parameters,
                                                   Object[] values,
                                                   int pages,
                                                   int linePerPage)
        {
            return (IPageList<T>)this.ExecuteFind(new GetLimitHibernateCallback<T>(queryStr, parameters, values, pages, linePerPage, true));
        }
        #endregion
    }

    #region Internal Supporting Callback Classes
    internal class LoadLimitAllByTypeHibernateCallback<T> : IFindHibernateCallback<T>
    {
        private HibernateTemplate outer;
        private int _pageIndex;
        private int _linePerPage;

        public LoadLimitAllByTypeHibernateCallback(HibernateTemplate template, int pageindex, int linePerPage)
        {
            outer = template;
            _pageIndex = pageindex;
            _linePerPage = linePerPage;
        }

        /// <summary>
        /// Gets called by HibernateTemplate with an active
        /// Hibernate Session. Does not need to care about activating or closing
        /// the Session, or handling transactions.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Allows for returning a result object created within the callback, i.e.
        /// a domain object or a collection of domain objects. Note that there's
        /// special support for single step actions: see HibernateTemplate.find etc.
        /// </p>
        /// </remarks>
        public IList<T> DoInHibernate(ISession session)
        {
            ICriteria criteria = session.CreateCriteria(typeof(T));
            outer.PrepareCriteria(criteria);

            // get total count
            int totalCount = criteria.List<T>().Count;

            if (_pageIndex < 1) _pageIndex = 1;
            IPageList<T> list = new PageList<T>(totalCount, _pageIndex, _linePerPage);
            criteria.SetFirstResult((_pageIndex - 1) * _linePerPage);
            criteria.SetMaxResults(_linePerPage);

            list.AddList(criteria.List<T>());
            return list;
        }
    }

    internal class GetLimitHibernateCallback<T> : IFindHibernateCallback<T>
    {
        private string queryStr;
        private string[] parameters;
        private Object[] values;

        private int pages = 0;
        private int linePerPage = 0;

        // 1-no param; 2-one param; 3-multi param
        private int m_Type = 1; 

        // if use HQL or namedquery
        private bool m_useHQL = false;


        public GetLimitHibernateCallback(string queryStr, int pages, int linePerPage, bool isUseHQL)
        {
            this.queryStr = queryStr;
            this.parameters = null;
            this.values = null;
            m_Type = 1;
            this.pages = pages;
            this.linePerPage = linePerPage;

            m_useHQL = isUseHQL;
        }

        public GetLimitHibernateCallback(string queryStr, string parameter, Object value, int pages, int linePerPage, bool isUseHQL)
        {
            this.queryStr = queryStr;
            this.parameters = new string[] { parameter };
            this.values = new Object[] { value };
            m_Type = 2;
            this.pages = pages;
            this.linePerPage = linePerPage;

            m_useHQL = isUseHQL;
        }

        public GetLimitHibernateCallback(string queryStr, string[] parameters, Object[] values, int pages, int linePerPage, bool isUseHQL)
        {
            this.queryStr = queryStr;
            this.parameters = parameters;
            this.values = values;
            m_Type = 3;
            this.pages = pages;
            this.linePerPage = linePerPage;

            m_useHQL = isUseHQL;
        }

        /// <summary>
        /// Gets called by HibernateTemplate with an active
        /// Hibernate Session. Does not need to care about activating or closing
        /// the Session, or handling transactions.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Allows for returning a result object created within the callback, i.e.
        /// a domain object or a collection of domain objects. Note that there's
        /// special support for single step actions: see HibernateTemplate.find etc.
        /// </p>
        /// </remarks>
        public IList<T> DoInHibernate(ISession session)
        {
            try
            {
                IQuery query;
                if (m_useHQL)
                {
                    query = session.CreateQuery(queryStr);
                }
                else
                {
                    query = session.GetNamedQuery(queryStr);
                }

                string sql = SQLToolsHelper.GetCountSQL(query.QueryString.Trim());
                IQuery query_count = session.CreateQuery(sql);

                if (m_Type > 1)
                {
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        query_count.SetParameter(parameters[i], values[i]);
                        query.SetParameter(parameters[i], values[i]);
                    }
                }
                
                // get total count
                int totalCount = (int)query_count.UniqueResult();
                
                if (pages < 1) pages = 1;
                IPageList<T> list = new PageList<T>(totalCount, pages, linePerPage);
                query.SetFirstResult((pages - 1) * linePerPage).SetMaxResults(linePerPage);
                list.AddList(query.List<T>());
                return list;
            }
            catch
            {
                return null;
            }
        }
    }

    #endregion

    public class SQLToolsHelper
    {
        public static string GetCountSQL(string sql)
        {
            int f1 = sql.IndexOf("From");
            if (f1 < 0)
            {
                f1 = sql.IndexOf("from");
            }
            if (f1 < 0)
            {
                f1 = sql.IndexOf("FROM");
            }
            if (f1 > 0)
            {
                sql = sql.Substring(f1);
            }
            sql = "SELECT Count(*) " + sql;
            int searchOrdery = sql.IndexOf("ORDER BY");
            if (searchOrdery >= 0)
            {
                sql = sql.Substring(0, searchOrdery - 1);
            }
            return sql;
        }
    }
}
